package comm

import (
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"
)

type reqRecord struct {
	mapUsrQps map[string]uint64
	startTime time.Time
}

const chkInterval = time.Minute

var rr = reqRecord{mapUsrQps: make(map[string]uint64), startTime: time.Now()}

var mutex = &sync.Mutex{}

type userinfo struct {
	pwd string
	qps uint64
}

var cmapUsrQps map[string]userinfo = map[string]userinfo{
	"user":  userinfo{pwd: "pass", qps: 5},
	"user2": userinfo{pwd: "pass2", qps: 100}}

func Logger(inner http.Handler, name string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		inner.ServeHTTP(w, r)

		log.Printf(
			"%s\t%s\t%s\t%s",
			r.Method,
			r.RequestURI,
			name,
			time.Since(start),
		)
	})
}

func BasicAuth(inner http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if usr := checkAuth(w, r); usr != "" {
			if ok, now, limit := overLoadProtect(usr); ok {
				inner.ServeHTTP(w, r)
			} else {
				w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
				w.WriteHeader(http.StatusForbidden)
				s := fmt.Sprintf("Over load protection, check interval:%dmin, limit:%d, qps:%d",
					chkInterval/time.Minute, limit, now)
				w.Write([]byte(s))
			}
		} else {
			w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
			w.WriteHeader(401)
			w.Write([]byte("401 Unauthorized\n"))
		}
	})
}

func checkAuth(w http.ResponseWriter, r *http.Request) string {
	usr, pwd, ok := r.BasicAuth()
	if ok {
		if ui, ok := cmapUsrQps[usr]; ok {
			if ui.pwd == pwd {
				return usr
			}
		}
	}
	return ""
}

func overLoadProtect(usr string) (bool, uint64, uint64) {
	mutex.Lock()
	defer mutex.Unlock()

	now := time.Now()
	if rr.startTime.Add(chkInterval).Before(now) {
		rr.startTime = now
		for k := range rr.mapUsrQps {
			rr.mapUsrQps[k] = 0
		}
	} else {
		if _, ok := rr.mapUsrQps[usr]; ok {
			rr.mapUsrQps[usr] += 1
			if ui, ok := cmapUsrQps[usr]; ok {
				if rr.mapUsrQps[usr] > ui.qps {
					return false, rr.mapUsrQps[usr], ui.qps
				}
			} else {
				// no user, should never be here
				return false, 0, 0
			}
		} else {
			rr.mapUsrQps[usr] = 1
		}
	}

	return true, 0, 0
}
